package com.skd.mall.product.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.skd.mall.product.dao.CategoryBrandRelationDao;
import com.skd.mall.product.entity.BrandEntity;
import com.skd.mall.product.entity.CategoryBrandRelationEntity;
import com.skd.mall.product.vo.Catalog2VO;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.skd.common.utils.PageUtils;
import com.skd.common.utils.Query;

import com.skd.mall.product.dao.CategoryDao;
import com.skd.mall.product.entity.CategoryEntity;
import com.skd.mall.product.service.CategoryService;


@Service("categoryService")
public class CategoryServiceImpl extends ServiceImpl<CategoryDao, CategoryEntity> implements CategoryService {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<CategoryEntity> page = this.page(
                new Query<CategoryEntity>().getPage(params),
                new QueryWrapper<CategoryEntity>()
        );

        return new PageUtils(page);
    }

    /*查询所有的子类并返回树形
    * */
    @Override
    public List<CategoryEntity> queryCategoryWithTree(Map<String, Object> params) {
        //查询所有种类
        List<CategoryEntity> allCategorys = baseMapper.selectList(null);
        //创建流，查询所有一级类型
        List<CategoryEntity> list = allCategorys.stream().filter(categoryEntity ->
                        categoryEntity.getParentCid() == 0)
                .map(categoryEntity -> {
                    //查询小类，并用 List childrens接收
                    categoryEntity.setChildrens(getChildrenList(categoryEntity,allCategorys));
                    return categoryEntity;
                } ).sorted((entity1,entity2) -> {
                    return (entity1.getSort() == null?0:entity1.getSort())- (entity2.getSort() == null?0:entity2.getSort());
                }).collect(Collectors.toList());
        return list;
    }

    @Override
    public void removeByIdsWithLogic(List<Long> list) {
        baseMapper.deleteBatchIds(list);
    }

    @Override
    public Long[] findCatelogPath(Long catelogId) {
        List<Long> list = new ArrayList<>();
        List<Long> parentPath = findParentPath(catelogId, list);
        return parentPath.toArray(new Long[parentPath.size()]);
    }



    public List<Long> findParentPath(Long catelogId,List<Long> list){
        list.add(catelogId);
        CategoryEntity entity = this.getById(catelogId);
        if (entity.getParentCid() != 0){
            findParentPath(entity.getParentCid(),list);
        }
        return list;
    }

    private List<CategoryEntity> getChildrenList(CategoryEntity categoryEntity, List<CategoryEntity> allCategorys) {
        List<CategoryEntity> list = allCategorys.stream().filter(categoryEntity1 -> {
            return  categoryEntity.getCatId().equals(categoryEntity1.getParentCid());
        }).map(categoryEntity1 -> {
            categoryEntity1.setChildrens(getChildrenList(categoryEntity1,allCategorys));
            return categoryEntity1;
        }).sorted((entity1, entity2) -> {
            return (entity1.getSort() == null ? 0 : entity1.getSort()) - (entity2.getSort() == null ? 0 : entity2.getSort());
        }).collect(Collectors.toList());
        return list;
    }
    @Autowired
    private CategoryBrandRelationDao categoryBrandRelationDao;
    QueryWrapper<CategoryBrandRelationEntity> wrapper = new QueryWrapper<>();
    @Override
    public void updateDetail(CategoryEntity category) {
        //更新原始数据
        this.updateById(category);
        //更新冗余数据
        wrapper.eq("catelog_id",category.getCatId());
        List<CategoryBrandRelationEntity> list = categoryBrandRelationDao.selectList(wrapper);
        list.stream().forEach(categoryBrandRelationEntity -> {
            if(!categoryBrandRelationEntity.getCatelogName().equals(category.getName())){
                categoryBrandRelationEntity.setCatelogName(category.getName());
                categoryBrandRelationDao.updateById(categoryBrandRelationEntity);
            }
        });
    }

    public List<CategoryEntity> queryByParentId(List<CategoryEntity> list,Long parentId){
        List<CategoryEntity> collect = list.stream().filter(item -> {
            return item.getParentCid().equals(parentId);
        }).collect(Collectors.toList());
        return collect;
    }


    @Override
    public Map<String, List<Catalog2VO>> getCatalog2JSON() {
        String key = "catalogJSON";
        //从缓存里查找
        String catalog2JSON = stringRedisTemplate.opsForValue().get(key);
        if(StringUtils.isEmpty(catalog2JSON)){
            //没有的话从数据库中查找
            Map<String, List<Catalog2VO>> catelog2JSONForDb = this.getCatelog2JSONDbWithRedisLock();
            if(catelog2JSONForDb == null){
                // 那就说明数据库中也不存在  防止缓存穿透
                stringRedisTemplate.opsForValue().set(key,"1",5, TimeUnit.SECONDS);
            }else {
                // 从数据库中查询到的数据，我们需要给缓存中也存储一份
                // 防止缓存雪崩
                String json = JSON.toJSONString(catelog2JSONForDb);
                stringRedisTemplate.opsForValue().set("catalogJSON",json,10,TimeUnit.MINUTES);
            }

            return catelog2JSONForDb;
        }
        // 表示缓存命中了数据，那么从缓存中获取信息，然后返回
        Map<String, List<Catalog2VO>> listMap = JSON.parseObject(catalog2JSON, new TypeReference<Map<String, List<Catalog2VO>>>() {
        });
        return listMap;
    }


    /**
     * 加分布式锁
     * */
    public Map<String,List<Catalog2VO>> getCatelog2JSONDbWithRedisLock(){
        String keys = "catalogJSON";
        // 加锁 在执行插入操作的同时设置了过期时间
        String uuid = UUID.randomUUID().toString();
        Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent("lock", uuid,300,TimeUnit.SECONDS);
        if(lock){
            Map<String, List<Catalog2VO>> data = null;
            try {
                // 加锁成功
                data = getDataForDB();
            }finally {
                String srcipts = "if redis.call('get',KEYS[1]) == ARGV[1]  then return redis.call('del',KEYS[1]) else  return 0 end ";
                // 通过Redis的lua脚本实现 查询和删除操作的原子性
                stringRedisTemplate.execute(new DefaultRedisScript<Long>(srcipts,Long.class)
                        , Arrays.asList("lock"),uuid);
            }
            return data;
        }else{
            // 加锁失败
            // 休眠+重试
            // Thread.sleep(1000);
            return getCatelog2JSONDbWithRedisLock();
        }
    }

    public Map<String,List<Catalog2VO>> getDataForDB(){
        //查询所有分类
        List<CategoryEntity> ls = this.list(new QueryWrapper<CategoryEntity>());
        //查询所有的一级分类
        List<CategoryEntity> l1s = this.queryByParentId(ls,0L);
        Map<String, List<Catalog2VO>> map = l1s.stream().collect(Collectors.toMap(
                key ->key.getCatId().toString(),
                value ->{
                    //查询2级分类集合
                    List<CategoryEntity> l2s = this.queryByParentId(ls,value.getCatId());
                    List<Catalog2VO> l2vos = null;
                    //把2级分类集合 包装成 2级vo类集合
                    if (l2s != null){
                        l2vos = l2s.stream().map(l2 -> {
                            Catalog2VO catalog2VO = new Catalog2VO(
                                    l2.getParentCid().toString(), null, l2.getCatId().toString(), l2.getName());
                            //找到3级分类
                            List<CategoryEntity> l3s = this.queryByParentId(ls,l2.getCatId());
                            List<Catalog2VO.Catalog3VO> l3vos = null;
                            if (l3s!=null){
                                //包装成3级vo
                                l3vos = l3s.stream().map(l3 -> {
                                    Catalog2VO.Catalog3VO l3vo = new Catalog2VO.Catalog3VO(
                                            l3.getParentCid().toString(), l3.getCatId().toString(), l3.getName());
                                    return l3vo;
                                }).collect(Collectors.toList());
                                //2级vo分类关联3级vo分类
                                catalog2VO.setCatalog3List(l3vos);
                            }
                            return catalog2VO;
                        }).collect(Collectors.toList());
                    }
                    //查询3级分类
                    return l2vos;
                }
        ));
        return map;
    }

    /**
     * <一级分类编号，二级分类vo集合>
     * */
    public synchronized Map<String, List<Catalog2VO>> getCatalog2JSONForDB() {
        //查询所有分类
        List<CategoryEntity> ls = this.list(new QueryWrapper<CategoryEntity>());
        //查询所有的一级分类
        List<CategoryEntity> l1s = this.queryByParentId(ls,0L);
        Map<String, List<Catalog2VO>> map = l1s.stream().collect(Collectors.toMap(
                key ->key.getCatId().toString(),
                value ->{
                    //查询2级分类集合
                    List<CategoryEntity> l2s = this.queryByParentId(ls,value.getCatId());
                    List<Catalog2VO> l2vos = null;
                    //把2级分类集合 包装成 2级vo类集合
                    if (l2s != null){
                        l2vos = l2s.stream().map(l2 -> {
                            Catalog2VO catalog2VO = new Catalog2VO(
                                    l2.getParentCid().toString(), null, l2.getCatId().toString(), l2.getName());
                            //找到3级分类
                            List<CategoryEntity> l3s = this.queryByParentId(ls,l2.getCatId());
                            List<Catalog2VO.Catalog3VO> l3vos = null;
                            if (l3s!=null){
                                //包装成3级vo
                                l3vos = l3s.stream().map(l3 -> {
                                    Catalog2VO.Catalog3VO l3vo = new Catalog2VO.Catalog3VO(
                                            l3.getParentCid().toString(), l3.getCatId().toString(), l3.getName());
                                    return l3vo;
                                }).collect(Collectors.toList());
                                //2级vo分类关联3级vo分类
                                catalog2VO.setCatalog3List(l3vos);
                            }
                            return catalog2VO;
                        }).collect(Collectors.toList());
                    }
                    //查询3级分类
                    return l2vos;
                }
        ));
        return map;
    }
}